using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

namespace System.Drawing
{
    public static class BitmapEx
    {
        static class API
        {
            [DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] // 3 = Unicode
            [ResourceExposure(ResourceScope.None)]
            public static extern int GdipBitmapSetPixel(HandleRef bitmap, int x, int y, int argb);

            [DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] // 3 = Unicode
            [ResourceExposure(ResourceScope.None)]
            public static extern int GdipBitmapGetPixel(HandleRef bitmap, int x, int y, out int argb);
        }


        private static Func<Bitmap, int, int, Color> PixelGeter;

        private static Func<Bitmap, IntPtr> NativeImageGetter;

        private static Color ColorGetter(Bitmap b, int x, int y)
        {
            int color;
            API.GdipBitmapGetPixel(new HandleRef(b, NativeImageGetter(b)), x, y, out color);
            return Color.FromArgb(color);
        }

        public static Color GetPixelEx(this Bitmap bitmap, int x, int y)
        {
            if(PixelGeter != null)
                return PixelGeter(bitmap, x, y);

            CreateNativeImageGetter();
            Expression<Func<Bitmap, int, int, Color>> e = (b, xx, yy) => ColorGetter(bitmap, x, y);
            PixelGeter = e.Compile();
            return PixelGeter(bitmap, x, y);
        }

        public static Func<int, int, Color> CreatePixelGetter(this Bitmap bitmap)
        {
            CreateNativeImageGetter();
            Expression<Func<int, int, Color>> expr = (x, y) => ColorGetter(bitmap, x, y);
            return expr.Compile();
        }

        private static void CreateNativeImageGetter()
        {
            if(NativeImageGetter != null) return;
            var BitmapType = typeof(Bitmap);
            var imabeP = Expression.Parameter(BitmapType, "b");
            var nativeImageFieldInfo = BitmapType.GetField("nativeImage", BindingFlags.Instance | BindingFlags.NonPublic);
            Debug.Assert(nativeImageFieldInfo != null, "nativeImageFieldInfo != null");
            var expr = Expression.Lambda<Func<Bitmap, IntPtr>>(Expression.Field(imabeP, nativeImageFieldInfo), imabeP);
            NativeImageGetter = expr.Compile();
        }

        private static Action<Bitmap, int, int, Color> PixelSetter;

        public static void SetPixelEx(this Bitmap bitmap, int x, int y, Color color)
        {
            if(PixelSetter != null)
                PixelSetter(bitmap, x, y, color);
            CreateNativeImageGetter();

            Expression<Action<Bitmap, int, int, Color>> e = (b, xx, yy, c) => API.GdipBitmapSetPixel(new HandleRef(b, NativeImageGetter(b)), xx, yy, c.ToArgb());
            PixelSetter = e.Compile();
            PixelSetter(bitmap, x, y, color);
        }

        public static Action<int, int, Color> CreatePixelSetter(this Bitmap bitmap)
        {
            CreateNativeImageGetter();
            Expression<Action<int, int, Color>> e = (xx, yy, c) => API.GdipBitmapSetPixel(new HandleRef(bitmap, NativeImageGetter(bitmap)), xx, yy, c.ToArgb());
            return e.Compile();
        }
    }
}